﻿using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Web;
using System.Collections.Generic;
using System.Web.Security;
using System.Web.UI;
using System.Xml;
using BigfootMVC.Email;
using BigfootMVC.Helpers;

namespace BigfootMVC
{
    /// <summary>
    /// This is code derived from the opensource project BigfootMVC
    /// </summary>
    public class WebApp
    {

        /// <summary>
        /// Initializes the WebApp by specifying the root path as being ~/
        /// </summary>
        public WebApp()
        {
            AppPath = "~/";
            ContextProvider = new ASPContext();
            EmailServer = new ASPEmail();
        }

        /// <summary>
        /// Initializes the WebApp by specifying the root path
        /// </summary>
        /// <param name="appPath">The relative root path i.e. ~/ or ~/desktopmodules/module/</param>
        public WebApp(string appPath)
        {
            _appPath = appPath;
            ContextProvider = new ASPContext();
            EmailServer = new ASPEmail();
        }

        /// <summary>
        /// Initializes the WebApp by specifying the root path as well as the email and context provider. Particularly useful when testing
        /// </summary>
        /// <param name="appPath">The relative root path i.e. ~/ or ~/desktopmodules/module/</param>
        /// <param name="context">This is the the IContext for the current instance of this application</param>
        /// <param name="emailProvider">This is the email provider for this instance of the application</param>
        public WebApp(string appPath, IContext context, IEmailProvider emailProvider)
        {
            _appPath = appPath;
            ContextProvider = context;
            EmailServer = emailProvider;
        }

        /// <summary>
        /// Stores the email of the administrator. It is used to send error reports etc. It is empty by default.
        /// </summary>
        public string AdminEmail { get; set; }

        #region Application Paths

        /// <summary>
        /// This is the root virtual path of where your applications files reside. This is the point from where 
        /// all content paths start. Example if you have all the content folder in ~/app/content/ then
        /// the AppPath would be ~/app/ . Always includes the last forward slash
        /// </summary>
        public string AppPath { 
            get { return (string.IsNullOrEmpty(_appPath)) ? "~/" : _appPath; }
            set { _appPath = value; }
        }
        private string _appPath;
        
        /// <summary>
        /// This is the data path of your applcation. This is the Data folder inside your application root
        /// </summary>
        public string DataPath
        {
            get { return string.IsNullOrEmpty(_dataPath) ? AppPath + "Data/" : _dataPath; }
            set { _dataPath = value; }
        }
        private string _dataPath;

        /// <summary>
        /// This is the path where the views of your applcation reside. They are in the Views folder inside your application root
        /// </summary>
        public string ViewsPath
        {
            get { return string.IsNullOrEmpty(_viewsPath) ? AppPath + "Views/" : _viewsPath; } 
            set { _dataPath = value; }
        }
        private string _viewsPath;

        /// <summary>
        /// This is the path where the content of your applcation reside like images, css, javascript, flash, and all other static files. 
        /// They are in the content folder folder inside your application root
        /// </summary>
        public string ContentPath
        {
            get { return string.IsNullOrEmpty(_contentPath) ? AppPath + "content/" : _contentPath; }
            set { _contentPath = value; }
        }
        private string _contentPath;

        /// <summary>
        /// This is the path to all your javascript files. AppPath/ContentPath/js/
        /// </summary>
        public string ScriptsPath
        {
            get { return string.IsNullOrEmpty(_scriptsPath) ? AppPath + "content/js/" : _scriptsPath; }
            set { _scriptsPath = value; }
        }
        private string _scriptsPath;

        /// <summary>
        /// This is the path to all your image files. AppPath/ContentPath/images/
        /// </summary>
        public string ImagesPath
        {
            get { return string.IsNullOrEmpty(_imagesPath) ? AppPath + "content/images/" : _imagesPath; }
            set { _imagesPath = value; }
        }
        private string _imagesPath;

        /// <summary>
        /// This is the path to all your CSS files. AppPath/ContentPath/css/
        /// </summary>
        public string CssPath
        {
            get { return string.IsNullOrEmpty(_cssPath) ? AppPath + "content/css/" : _cssPath; }
            set { _cssPath = value; }
        }
        private string _cssPath;

        /// <summary>
        /// This is the path to all your flash files. AppPath/ContentPath/FlashPath/
        /// </summary>
        public string FlashPath
        {
            get { return string.IsNullOrEmpty(_flashPath) ? AppPath + "content/flash/" : _flashPath; }
            set { _flashPath = value; }
        }
        private string _flashPath;

        /// <summary>
        /// This is the path to all your audio files. AppPath/ContentPath/AudioPath/
        /// </summary>
        public string AudioPath
        {
            get { return string.IsNullOrEmpty(_audioPath) ? AppPath + "content/audio/" : _audioPath; }
            set { _audioPath = value; }
        }
        private string _audioPath;

        #endregion


        /// <summary>
        /// Flag that determines that the application is in testing mode
        /// </summary>
        public bool InTestingMode { get; set; }

        /// <summary>
        /// This is the email provider to be used by the application
        /// </summary>
        public IEmailProvider EmailServer;

        /// <summary>
        /// This provides the application functions with context information. 
        /// For testing you can simply overwrite the context provider with your own mocked 
        /// objects and all the functionality is re-routed to your own testing mock
        /// </summary>
        public IContext ContextProvider;
        
        /// <summary>
        /// Access to the HttpRequest contained within the ContextProvider. Aliased here for convinience
        /// </summary>
        public HttpRequest Request { get { return ContextProvider.GetRequest(); } }

        /// <summary>
        /// Access to the HttpResponse contained within the ContextProvider. Aliased here for convinience
        /// </summary>
        public HttpResponse Response { get { return ContextProvider.GetResponse(); } }

        /// <summary>
        /// Access to the HttpContext contained within the ContextProvider. Aliased here for convinience
        /// </summary>
        public HttpContext Context { get { return ContextProvider.GetContext(); } }

        /// <summary>
        /// Access to the HttpServerUtility contained within the ContextProvider. Aliased here for convinience
        /// </summary>
        public HttpServerUtility Server { get { return ContextProvider.GetServer(); } }

        /// <summary>
        /// The name of the currently logged in principle
        /// </summary>
        public string UserName { get { return (Request.IsAuthenticated) ? Context.User.Identity.Name : ""; } }

        /// <summary>
        /// Determines weather we are in debug mode at compile time
        /// </summary>
        public bool IsInDebugMode { get {
            #if (DEBUG)
                return true;
            #else
                return false;
            #endif
        } }
        
        #region "Cache"

        /// <summary>
        /// This is a global application cache that persists accross requests while the application lives. Please takenote that it is server specific
        /// </summary>
        public static class Cache
        {
            static Dictionary<string, object> _cache = new Dictionary<string, object>();

            /// <summary>
            /// Determine weather a certain key is contained in the cache
            /// </summary>
            /// <param name="key">The key to check for</param>
            /// <returns>True if found</returns>
            public static bool Contains(String key) { return _cache.ContainsKey(key); }

            /// <summary>
            /// Returns the value stored in the cache
            /// </summary>
            /// <param name="key">The parameter key to the value</param>
            /// <returns>The cached value</returns>
            public static object GetValue(String key)
            {
                return (_cache.ContainsKey(key)) ? _cache[key] : null;
            }

            /// <summary>
            /// Adds a value to the cache. Replaces any excisting value if found
            /// </summary>
            /// <param name="key">The key of the value to add</param>
            /// <param name="data">The data to add to the cache</param>
            public static void Add(String key, object data)
            {
                if (_cache.ContainsKey(key))
                    _cache[key] = data;
                else
                    _cache.Add(key, data);
            }

            /// <summary>
            /// Removes a value from the cache safely. It does not throw an error if the value is not found.
            /// </summary>
            /// <param name="key">The key to the cahced value</param>
            public static void Remove(string key)
            {
                if (_cache.ContainsKey(key)) _cache.Remove(key);
            }
        }
        #endregion
        
        #region "Context.Items cache"

        public bool HasContextData(string key)
        {
            return Context.Items.Contains(key);
        }

        public object ContextData(string key)
        {
            return Context.Items.Contains(key) ? Context.Items[key] : null;
        }

        public void ContextData(string key, object value)
        {
            if (Context.Items.Contains(key))
                Context.Items[key] = value;
            else
                Context.Items.Add(key, value);
        }

        public static bool CurrentContext_HasData(string key)
        {
            return HttpContext.Current.Items.Contains(key);
        }

        public static object CurrentContext_GetData(string key)
        {
            return HttpContext.Current.Items.Contains(key) ? HttpContext.Current.Items[key] : null;
        }

        public static void CurrentContext_SetData(string key, object value)
        {
            if (HttpContext.Current.Items.Contains(key))
                HttpContext.Current.Items[key] = value;
            else
                HttpContext.Current.Items.Add(key, value);
        }


        #endregion

        /// <summary>
        /// Determines weather the current request is an ajax request
        /// </summary>
        public bool IsAjaxRequest
        {
            get
            {
                if (InTestingMode) return false;
                return Request.Headers["X-Requested-With"] == "XMLHttpRequest";
            }
        }

        /// <summary>
        /// Determines if the current user is authenticated
        /// </summary>
        public bool IsAuthenticated
        {
            get { return Request.IsAuthenticated; }
        }

        /// <summary>
        /// Gets a reference to the HTML Helper
        /// </summary>
        public HtmlHelper Html
        {
            get { return _html ?? (_html = HtmlHelper.Create()); }
        }
        private HtmlHelper _html;

        /// <summary>
        /// Gets a reference to the JS Helper
        /// </summary>
        public JSHelper JS
        {
            get { return _js ?? (_js = JSHelper.Create()); }
        }
        private JSHelper _js;
        

        #region "Path Functions"

        /// <summary>
        /// Gets a browser usable path to a content file within the system. It uses the extention of the file to determine the location of the static file
        /// within the content folder.
        /// i.e. "image.jpg" will return "/desktopmodules/modulename/content/images/image.jpg"
        /// i.e. "menu/left.jpg" will return "/desktopmodules/modulename/content/images/menu/left.jpg"
        /// i.e. "test.js" will return "/desktopmodules/modulename/content/js/test.js"
        /// </summary>
        /// <param name="fileName">The name of the file relative to the content folder i.e. left.jpg or menu/left.jpg</param>
        /// <returns>A url that may be used from the browser to access the static resource</returns>
        public string StaticUrl(string fileName)
        {
            return StaticUrl(fileName, false);
        }

        /// <summary>
        /// Gets a browser usable path to a content file within the system. It uses the extention of the file to determine the location of the static file
        /// within the content folder.
        /// i.e. "image.jpg" will return "/desktopmodules/modulename/content/images/image.jpg"
        /// i.e. "menu/left.jpg" will return "/desktopmodules/modulename/content/images/menu/left.jpg"
        /// i.e. "test.js" will return "/desktopmodules/modulename/content/js/test.js"
        /// i.e. "~/Test/test.js" will return /desktopmodules/modulename/Test/test.js"
        /// </summary>
        /// <param name="fileName">The name of the file relative to the content folder i.e. left.jpg or menu/left.jpg</param>
        /// <param name="includeDomain">Determines weather to include the domain name in the url. (i.e. http://domain.com/desktopmodules/modulename/content/image.jpg)</param>
        /// <returns>A url that may be used from the browser</returns>
        public string StaticUrl(string fileName, bool includeDomain)
        {
            var relUrl =  GetRelativeUrl(fileName);
			
			if (fileName.StartsWith("~/"))
                relUrl = AppPath + fileName.Substring(2);
            else if (fileName.StartsWith("/~/")) // This notation is used to signify the DNN application root
                relUrl = fileName.Substring(1);

            return GetAbsoluteUrl(relUrl, includeDomain);
        }

        /// <summary>
        /// Adds the authentication token to a url. Provided that you are doing cookieless authentication
        /// </summary>
        /// <param name="url">The url to use</param>
        /// <returns>Returns the modified url</returns>
        public string AddAuthTokenToUrl(string url)
        {
            return Response.ApplyAppPathModifier(url);
        }

        /// <summary>
        /// Retreives the relative url of a file based on its extention
        /// </summary>
        /// <param name="filename">filename: test.jpg returns ~/content/images/test.jpg</param>
        /// <returns>The server side relative url to the file</returns>
        public string GetRelativeUrl(string filename)
        {
            // Don't process files that already include the root element "~/" or the /~/
            if (filename.StartsWith("~/") || filename.StartsWith("/~/")) return filename;
			
            filename = filename.ToLower();
            if (filename.EndsWith("js"))
                return ScriptsPath + filename;
            else if (filename.EndsWith("css"))
                return CssPath + filename;
            else if (filename.EndsWith("jpg") || filename.EndsWith("gif") || filename.EndsWith("png"))
                return ImagesPath + filename;
            else if (filename.EndsWith("flv") || filename.EndsWith("swf"))
                return FlashPath + filename;
            else
                return filename;
        }

        /// <summary>
        /// Returns the server root application path. /AppLocation/ or / when at the root of the domain. This is the path to the application after the domain name
        /// </summary>
        /// <returns>Returns / or /appname/ or /apps/appname/ etc. What is after the domain name</returns>
        public string GetApplicationPath() { return GetApplicationPath(false); }

        /// <summary>
        /// Root application path. /app/ or / when at the root of the domain. 
        /// </summary>
        /// <param name="includeDomain">Determines weather the domain name is included</param>
        /// <returns>Returns the full application path root including the domain name if asked.</returns>
        public string GetApplicationPath(bool includeDomain)
        {
            var apppath = Request.ApplicationPath;
            if (apppath.EndsWith("/") == false) apppath += "/";

            if (includeDomain)
            {
                var host = GetApplicationHost;
                // Remove the host last "/"
                host = host.Substring(0, host.Length - 1);
                // Combine the application path and the host
                apppath = host + apppath;
            }
            return apppath;
        }

        
        /// <summary>
        /// Gets the application host name of the current request. Returns something like this http://applicationhost.com:4454 if it is port 80 then
        /// the port number is not used
        /// </summary>
        public string GetApplicationHost
        {
            get
            {
                var port = Request.ServerVariables["SERVER_PORT"];
                if (String.IsNullOrEmpty(port) || port == "80" || port == "443")
                    port = "";
                else
                    port = ":" + port;

                var protocol = Request.ServerVariables["SERVER_PORT_SECURE"];
                if (string.IsNullOrEmpty(protocol) || protocol == "0")
                    protocol = "http://";
                else
                    protocol = "https://";

                var domain = Request.ServerVariables["SERVER_NAME"];

                return protocol + domain + port + "/";
            }
        }

        /// <summary>
        /// Returns the physical path to a relative url. 
        /// For example url: ~/content/media/xyz.ppt returns c:\inetpub\wwwroot\appdomain.com\internal\content\media\xyz.ppt
        /// </summary>
        /// <param name="url">The relative application path i.e. ~/content/media/xyz.ppt</param>
        /// <returns>Full file system path i.e. c:\inetpub\wwwroot\appdomain.com\internal\content\media\xyz.ppt</returns>
        public string GetPhysicalPath(string url)
        {
            return Context.Server.MapPath(url);
        }

        /// <summary>
        /// Returns the absolute path to a url in the appliation. 
        /// For example url: ~/content/media/xyz.ppt returns /applicationpath/content/media/xyz.ppt
        /// </summary>
        /// <param name="url">The relative application path i.e. ~/content/media/xyz.ppt</param>
        /// <returns>Returns abosolute path to the url without the domain name. i.e. /applicationpath/content/media/xyz.ppt</returns>
        public string GetAbsoluteUrl(string url) { return GetAbsoluteUrl(url, false, false); }

        /// <summary>
        /// Returns the absolute path to a url in the appliation. 
        /// For example url: ~/content/media/xyz.ppt returns /applicationpath/content/media/xyz.ppt or http://applicationhsot/applicationpath/content/media/xyz.ppt
        /// </summary>
        /// <param name="url">The relative application path i.e. ~/content/media/xyz.ppt</param>
        /// <param name="includeFullPath">Determines weather to include the domain name</param>
        /// <returns>Returns abosolute path to the url without the domain name. i.e. /applicationpath/content/media/xyz.ppt
        /// or http://applicationhsot/applicationpath/content/media/xyz.ppt</returns>
        public string GetAbsoluteUrl(string url, bool includeFullPath)
        {
            return GetAbsoluteUrl(url, includeFullPath, false);
        }

        /// <summary>
        /// Returns the absolute path to a url in the appliation.  Optionally adds the url authentication token to the url
        /// For example url: ~/content/media/xyz.ppt returns /applicationpath/content/media/xyz.ppt or http://applicationhsot/applicationpath/content/media/xyz.ppt
        /// </summary>
        /// <param name="url">The relative application path i.e. ~/content/media/xyz.ppt</param>
        /// <param name="includeFullPath">Determines weather to include the domain name</param>
        /// <param name="addUrlAuthToken">Determines whether to add the url authentication token to the path</param>
        /// <returns>Returns abosolute path to the url without the domain name. i.e. /applicationpath/content/media/xyz.ppt
        /// or http://applicationhsot/applicationpath/content/media/xyz.ppt</returns>
        public string GetAbsoluteUrl(string url, bool includeFullPath, bool addUrlAuthToken)
        {
            var apppath = GetApplicationPath(includeFullPath);
            
            if (url.StartsWith("~/")) url = url.Substring(2);
            if (url.StartsWith("/")) url = url.Substring(1);
            
            url = apppath + url;

            if (addUrlAuthToken) url = AddAuthTokenToUrl(url); 

            return url;
        }

        /// <summary>
        /// Creates a unique file name for the path in the file. This prevents from duplicate file names (overriding). 
        /// Also creates the file path if it does not exist
        /// </summary>
        /// <param name="filePath">The full phisical path to the file</param>
        /// <returns>A full physicial path to a unique file name that may be used in the specified path</returns>
        public string GetUniqueFileName(string filePath)
        {
            // Make sure the file name is not empty
            if (string.IsNullOrEmpty(filePath))
                throw new ApplicationException("File Name is invalid!");

            // Ensure the path exists
            var folderPath = Path.GetDirectoryName(filePath) + '\\';
            if (!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath);

            // This will hold the final file name (without the directory after the unique algorithm is run through)
            var newFileName = Path.GetFileName(filePath);

            // Ensure the filename is unique in the directory
            if (File.Exists(folderPath + newFileName))
            {
                var fileNameWithoutExt = Path.GetFileNameWithoutExtension(newFileName);
                var ext = Path.GetExtension(newFileName);
                var count = 1;

                while (File.Exists(folderPath + fileNameWithoutExt + count + ext))
                {
                    count++;
                }

                newFileName = fileNameWithoutExt + count + ext;
            }

            return folderPath + newFileName;
        }
        #endregion

        #region "Authentication"
        /// <summary>
        /// Gets the session token to be included in the url for sessionless authentication
        /// </summary>
        /// <returns>The token to be used in the url</returns>
        public string GetUrlAuthToken()
        {

            if (InTestingMode) return "TESTTOKEN";
            // Inserts the url token by calling this function
            var token = Response.ApplyAppPathModifier("~/");
            // The token uses the following format: /(F(*******)/
            return token.Substring(1, token.Substring(1).IndexOf("/"));
        }

        /// <summary>
        /// Authenticates the user against the membership provider setup in the web.config
        /// </summary>
        /// <param name="userName">The name of the user</param>
        /// <param name="password">The password</param>
        /// <returns>True if correctly authenticated</returns>
        public bool AuthenticateUser(string userName, string password)
        {
            var status = Membership.Provider.ValidateUser(userName, password);
            // Set the authentication cookie
            if (status && InTestingMode == false) FormsAuthentication.SetAuthCookie(userName, true);
            return status;
        }

        #endregion

        #region "Encryption"

        /// <summary>
        /// Encrypts a value with the specified key using DES encryption
        /// </summary>
        /// <param name="key">The key to use in the encryption</param>
        /// <param name="data">The data to encrypt</param>
        /// <returns>Base64 encrypted string</returns>
        public static string Encrypt(string key, string data)
        {
            // don't do anything if the key is empty
            if (string.IsNullOrEmpty(key)) return data;

            // normalize the key to 16 characters
            if (key.Length < 16)
                key = key + "XXXXXXXXXXXXXXXX".Substring(0, 16 - key.Length);
            else
                key = key.Substring(0, 16);

            // create encryption key
            var bytekey = Encoding.UTF8.GetBytes(key.Substring(0, 8));
            var bytevector = Encoding.UTF8.GetBytes(key.Substring(8));

            // Convert the data
            var bytedata = Encoding.UTF8.GetBytes(data);

            // Encrypt
            var des = new DESCryptoServiceProvider();
            var memstream = new MemoryStream();
            var cryptostream = new CryptoStream(memstream, des.CreateEncryptor(bytekey, bytevector),
                                                CryptoStreamMode.Write);
            cryptostream.Write(bytedata, 0, bytedata.Length);
            cryptostream.FlushFinalBlock();

            // covert to string and base64 encode
            return Convert.ToBase64String(memstream.ToArray());
        }

        /// <summary>
        /// Decrypts a value with the specified key using DES encryption
        /// </summary>
        /// <param name="key">The key to use in the decryption</param>
        /// <param name="data">The data to decrypt base64 encoded</param>
        /// <returns>Decripted string</returns>
        public static string Decrypt(string key, string data)
        {
            // don't do anything if the key is empty
            if (string.IsNullOrEmpty(key)) return data;

            // normalize the key to 16 characters
            if (key.Length < 16)
                key = key + "XXXXXXXXXXXXXXXX".Substring(0, 16 - key.Length);
            else
                key = key.Substring(0, 16);

            // create encryption key
            var bytekey = Encoding.UTF8.GetBytes(key.Substring(0, 8));
            var bytevector = Encoding.UTF8.GetBytes(key.Substring(8));

            byte[] bytedata;
            try
            {
                bytedata = Convert.FromBase64String(data);
            }
            // return the data unchanged if there is some sort of error converting
            catch (Exception)
            {
                return data;
            }

            // Run the decryption
            try
            {
                var des = new DESCryptoServiceProvider();
                var ms = new MemoryStream();
                var cs = new CryptoStream(ms, des.CreateDecryptor(bytekey, bytevector), CryptoStreamMode.Write);
                cs.Write(bytedata, 0, bytedata.Length);
                cs.FlushFinalBlock();

                return Encoding.UTF8.GetString(ms.ToArray());
            }
            catch (Exception)
            {
                return "";
            }
        }

        #endregion

        #region "Values"

        /// <summary>
        /// Class used to aid in the collection of posted values from the request. First it tries the QueryString and second it tries the form collection
        /// </summary>
        public PostHelper Values
        {
            get
            {
                const string key = "poster_values";
                if (!HasContextData(key)) 
                    ContextData(key, new PostHelper(Context, ContextProvider.ManualValues, InTestingMode));

                return (PostHelper) ContextData(key);
            }
        }

        #endregion

        #region "Email"

        /// <summary>
        /// Sends an email using the email provider
        /// </summary>
        /// <param name="from">Who is the email from</param>
        /// <param name="to">Who is it going to. Can include multiple address separated by comma</param>
        /// <param name="subject">The subject line</param>
        /// <param name="body">The body of the html</param>
        /// <param name="isHtml">True if the body is using HTML</param>
        public void SendEmail(string from, string to, string subject, string body, bool isHtml)
        {
            var msg = new MailMessage(from, to, subject, body) { IsBodyHtml = isHtml };
            SendEmail(msg);
        }

        /// <summary>
        /// Sends an email using the email provider
        /// </summary>
        /// <param name="msg">The email message to send</param>
        public void SendEmail(MailMessage msg)
        {
            if (InTestingMode)
            {
                WebApp.Cache.Add("EmailSent", msg);
                return;
            }
            EmailServer.SendEmail(msg);
        }
        #endregion
 
        /// <summary>
        /// This flags indicates that we have implicitly called response.end and it does not log the exception
        /// thrown by premature response termination
        /// </summary>
        public static bool ResponseEndCalled
        {
            get
            {
                return HttpContext.Current.Items.Contains("ResponseEndCalled") && (bool)HttpContext.Current.Items["ResponseEndCalled"];
            }
            set
            {
                if (HttpContext.Current.Items.Contains("ResponseEndCalled"))
                    HttpContext.Current.Items["ResponseEndCalled"] = value;
                else
                    HttpContext.Current.Items.Add("ResponseEndCalled", value);
            }
        }


        #region "Render"
        
        public void RenderViewToResponse(string viewRelativePath, object data)
        {
            WebApp.RenderViewToResponse(Server, Response, viewRelativePath, data, null, null);
        }

        public void RenderViewToResponse(string viewRelativePath, object data, string loadField1Name, object loadField1Data)
        {
            WebApp.RenderViewToResponse(Server, Response, viewRelativePath, data, loadField1Name, loadField1Data);
        }

        public static void RenderViewToResponse(HttpServerUtility server, HttpResponse response, string viewRelativePath, object data, string loadField1Name, object loadField1Data)
        {
            // Render the page to the output stream of the response provided
            server.Execute(RenderViewToPage(viewRelativePath, data, loadField1Name, loadField1Data), response.Output, true);
        }

        public string RenderViewToString(string viewRelativePath, object data)
        {
            return WebApp.RenderViewToString(Server, viewRelativePath, data, null, null);
        }

        public string RenderViewToString(string viewRelativePath, object data, string loadField1Name, object loadField1Data)
        {
            return WebApp.RenderViewToString(Server, viewRelativePath, data, loadField1Name, loadField1Data);
        }

        public static string RenderViewToString(HttpServerUtility server, string viewRelativePath, object data, string loadField1Name, object loadField1Data)
        {
            // Render the page
            var output = new StringWriter();

            // Render the page to the output stream
            server.Execute(RenderViewToPage(viewRelativePath, data, loadField1Name, loadField1Data), output, true);

            // Return the rendered output
            return output.ToString();
        }

        private static Page RenderViewToPage(string viewRelativePath, object data, string loadField1Name, object loadField1Data)
        {
            // Load the control
            var pageHolder = new Page();
            var viewControl = (UserControl)pageHolder.LoadControl(viewRelativePath);

            // Get the type of the control
            var t = viewControl.GetType();

            // Set the model data
            SetField(viewControl, t, "ModelData", data);

            // Flag the render view as being partially rendered
            SetField(viewControl, t, "IsRenderPartial", true);
            
            // Add additional data if specified
            SetField(viewControl, t, loadField1Name, loadField1Data);

            // Load the control on the page
            pageHolder.Controls.Add(viewControl);

            return pageHolder;
        }

        private static void SetField(object control, Type t, string fieldName, object data)
        {
            // Add additional data if specified
            if (string.IsNullOrEmpty(fieldName) || data == null) return;
            var field = t.GetField(fieldName);
            if (field != null)
            {
                field.SetValue(control, data);
            }
            else // Check if it is a property
            {
                var prop = t.GetProperty(fieldName);
                if (prop != null)
                    prop.SetValue(control, data, BindingFlags.SetProperty, null, null, null);
            }
        }

        public static UserControl LoadView(string viewPath, object data)
        {
            // Load the control
            var pageHolder = new Page();
            return LoadView(viewPath, data, pageHolder);
        }

        public static UserControl LoadView(string viewPath, object data, Page currentPage)
        {
            return LoadView(viewPath, data, currentPage, null, null);
        }

        public static UserControl LoadView(string viewPath, object data, Page currentPage, string loadField1Name, object loadField1Data)
        {
            var ctl = (UserControl)currentPage.LoadControl(viewPath);

            // Get the type of the control
            var t = ctl.GetType();

            // Set the model data
            SetField(ctl, t, "ModelData", data);

            // Flag the render view as being partially rendered
            SetField(ctl, t, "IsRenderPartial", true);

            // Add additional data if specified
            SetField(ctl, t, loadField1Name, loadField1Data);
            return (UserControl)ctl;
        }

        public void RenderJson(string data)
        {
            WebApp.RenderJson(Response, data);
        }

        public static void RenderJson(HttpResponse response, string data)
        {
            response.Clear();
            WebApp.SetNoCache(response);
            response.ContentType = "application/json";
            response.Write(data);
            ResponseEndCalled = true;
            response.End();
        }

        public void RenderLiteral(string data)
        {
            WebApp.RenderLiteral(Response, data);
        }

        public static void RenderLiteral(HttpResponse response, string data)
        {
            response.Clear();
            WebApp.SetNoCache(response);
            response.Write(data);
            ResponseEndCalled = true;
            response.End();
        }

        public void SetNoCache()
        {
            WebApp.SetNoCache(Response);
        }

        public static void SetNoCache(HttpResponse response)
        {
            response.Expires = 0;
            response.Cache.SetNoServerCaching();
            response.Cache.SetNoStore();
            response.AppendHeader("Pragma", "no-cache");
        }
        
        #endregion

        #region "REST Functions"

        public string PostValues(string url, NameValueCollection postdata)
        {
            var wc = new WebClient();
            ServicePointManager.Expect100Continue = false;
            var returnData = wc.UploadValues(url, "POST", postdata);
            return (new StreamReader(new MemoryStream(returnData))).ReadToEnd();
        }

        public XmlDocument PostValuesAndReturnXml(string url, NameValueCollection postData )
        {
            var doc = new XmlDocument();
            doc.LoadXml(PostValues(url, postData));
            return doc;
        }
        #endregion
        
        #region "Local Strings"

        /// <summary>
        /// This delegate is used to get a localized string
        /// </summary>
        /// <param name="resourceName">The name of the string resource to retreive</param>
        /// <returns>The localized string</returns>
        public delegate string LocalStringDelegate(string resourceName);

        /// <summary>
        /// Sets the local string delegate to be called when requiring a localized string
        /// </summary>
        /// <param name="methodDelegate">The method to call</param>
        public void SetLocalStringDelegate(LocalStringDelegate methodDelegate)
        {
            _localString = methodDelegate;
        }
        private LocalStringDelegate _localString;

        /// <summary>
        /// Retreives a local string using either the assigned LocalStringDelegate through SetLocalStringDelegate
        /// or returns the defaultValue provided
        /// </summary>
        /// <param name="resourceName">The name of the resource to retreive</param>
        /// <param name="defaultValue">The value to return if no delegate has been specified</param>
        /// <returns>The localized string</returns>
        public string LocalString(string resourceName, string defaultValue)
        {
            var val = (_localString != null) ? _localString.Invoke(resourceName) : "";
            if (string.IsNullOrEmpty(val)) val = defaultValue;
            return val;
        }

        /// <summary>
        /// Gets the instance of the LocalStrings being used in this app
        /// </summary>
        public MVCLocalStrings MVCStrings { get { return _mvcStrings ?? (_mvcStrings = new MVCLocalStrings(this)); } }
        private MVCLocalStrings _mvcStrings;

        /// <summary>
        /// This method should be called in order to configure the local resource strings
        /// </summary>
        public void SetupLocalResources()
        {
            MVCStrings.LoadValidationLanguage();
            Values.Message_ExpectedValueNotFound = MVCStrings.PostValRequired;
        }

        #endregion

        #region "HandleException"

        public void HandleException(Exception exception)
        {
            HandleException(exception, Context);
        }

        public void HandleException(Exception exception, HttpContext context)
        {
            var httpException = exception as HttpException;
            if (httpException != null && (httpException.GetHttpCode() == 405 || httpException.GetHttpCode() == 404 || httpException.GetHttpCode() == 403))
            {
                context.Response.StatusCode = httpException.GetHttpCode();
                context.Response.SuppressContent = true;
                context.Response.End();
                return;
            }


            var errorMessage = new StringBuilder();

            if (exception is HttpUnhandledException)
                exception = exception.InnerException;

            errorMessage.Append(exception.ToString());

            if (httpException != null)
                errorMessage.Append("\n\nHTTP EXCEPTION CODE: " + httpException.GetHttpCode());

            if (exception.InnerException != null)
            {
                errorMessage.Append("\n\n ***INNER EXCEPTION*** \n");
                errorMessage.Append(exception.InnerException.ToString());
            }

            if (context != null)
            {
                //if (context.Request.IsLocal)
                //    return;
                errorMessage.AppendFormat("\n\nRequest Path = {0}\n", context.Request.Url);

                errorMessage.Append("\n\n ***REQUEST PARAMETERS*** \n");
                foreach (string LName in context.Request.Params.Keys)
                {
                    errorMessage.AppendFormat("\n{0} = {1};", LName, context.Request[LName]);
                }

                if (context.Session != null)
                {
                    errorMessage.Append("\n\n ***SESSION VARIABLES*** \n");
                    foreach (string key in context.Session.Keys)
                    {
                        errorMessage.AppendFormat("\n{0} = {1};", key, context.Session[key]);
                    }
                }
            }

            System.Diagnostics.Debug.Print(errorMessage.ToString());

            // Send and email to the admin if he exists
            if (!string.IsNullOrEmpty(AdminEmail))
            {
                SendEmail(
                    "System Admin<" + AdminEmail + ">",
                    AdminEmail,
                    "Application Error " + Guid.NewGuid(),
                    errorMessage.ToString(),
                    false
                );
            }

        }

        #endregion

    }

}
